package com.vegetable.modules.service.outbound.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.vegetable.common.constant.CommonConstant;
import com.vegetable.common.annotation.SalesDataPermission;
import com.vegetable.common.annotation.SalesOutChangeTracking;
import com.vegetable.common.utils.R;
import com.vegetable.modules.entity.baseData.AccountDetails;
import com.vegetable.modules.entity.baseData.AddressBook;
import com.vegetable.modules.entity.baseData.Products;
import com.vegetable.modules.entity.finance.FinancialCollectionDetail;
import com.vegetable.modules.entity.finance.po.ReconciliationPO;
import com.vegetable.modules.entity.finance.vo.FinancialCollectionDetailVO;
import com.vegetable.modules.entity.inbound.AgentDeliveryDetailPack;
import com.vegetable.modules.entity.inbound.OrderCost;
import com.vegetable.modules.entity.outbound.*;
import com.vegetable.modules.entity.outbound.excel.SalesOutDetailExport;
import com.vegetable.modules.entity.outbound.excel.SalesOutImport;
import com.vegetable.modules.entity.outbound.po.SalesOutQueryPO;
import com.vegetable.modules.entity.outbound.po.SalesOutStatisticsPO;
import com.vegetable.modules.entity.outbound.po.UpdateHedgingStatePO;
import com.vegetable.modules.entity.outbound.po.UpdateOrderTimePO;
import com.vegetable.modules.entity.outbound.vo.SalesOutDetailVO;
import com.vegetable.modules.entity.outbound.vo.SalesOutGeneralVO;
import com.vegetable.modules.entity.outbound.vo.SalesOutStatisticsVO;
import com.vegetable.modules.entity.outbound.vo.SalesOutPageVO;
import com.vegetable.modules.entity.allocation.AllocationCollection;
import com.vegetable.modules.entity.allocation.AllocationCollectionDetail;
import com.vegetable.modules.entity.allocation.vo.AllocationCollectionDetailWithSalesInfo;
import com.vegetable.modules.mapper.baseData.VehicleManagementMapper;
import com.vegetable.modules.mapper.outbound.SalesOrderMapper;
import com.vegetable.modules.mapper.outbound.SalesOutMapper;
import com.vegetable.modules.service.baseData.*;
import com.vegetable.modules.service.finance.IFinancialCollectionDetailService;
import com.vegetable.modules.service.inbound.IAgentDeliveryDetailPackService;
import com.vegetable.modules.service.inbound.IOrderCostService;
import com.vegetable.modules.service.outbound.*;
import com.vegetable.modules.service.quotation.IQuotationService;
import com.vegetable.modules.service.stock.IStockService;
import com.vegetable.modules.service.sys.SysUserService;
import com.vegetable.modules.service.allocation.IAllocationCollectionService;
import com.vegetable.modules.vo.finance.ArrearsDetailVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 销售出库单 服务实现类
 * </p>
 *
 * @author wangruijie
 * @since 2024-04-01
 */
@Slf4j
@Service
//@AllArgsConstructor
public class SalesOutServiceImpl extends ServiceImpl<SalesOutMapper, SalesOut> implements ISalesOutService {

    // 订单费用
    @Lazy
    @Autowired
    private IOrderCostService orderCostService;
    @Autowired
    private ISalesOutDetailService detailService;
    // 销售商品明细库存对照表
    @Autowired
    private ISalesOrderDetailStockService detailStockService;
    @Autowired
    private IAgentDeliveryDetailPackService iAgentDeliveryDetailPackService;
    // 产地商品
    @Autowired
    private IProductsService productsService;
    // 库存管理
    @Autowired
    private IStockService stockService;
    // 通讯录
    @Autowired
    private IAddressBookService addressBookService;
    // 往来明细
    @Autowired
    private IAccountDetailsService accountDetailsService;
    @Autowired
    private IProductSpecService productSpecService;
    @Autowired
    private VehicleManagementMapper carMapper;
    // 库存流水
    @Autowired
    private ISalesOrderService salesOrderService;
    @Autowired
    private ISalesOrderDetailService iSalesOrderDetailService;
    @Autowired
    private ITrainNumRecordService iTrainNumRecordService;
    @Autowired
    private IUnitPackagePriceRecordService iUnitPackagePriceRecordService;
    @Autowired
    private ISalesOutCarryOnService iSalesOutCarryOnService;
    @Autowired
    private IQuotationService iQuotationService;
    @Autowired
    private SalesOrderMapper salesOrderMapper;
    
    // 系统用户服务
    @Autowired
    private SysUserService sysUserService;
    
    // 分配回款服务
    @Autowired
    private IAllocationCollectionService allocationCollectionService;

    /**
     * 销售出库列表查询
     *
     * @param po
     * @return
     */
    @Override
    @SalesDataPermission(type = SalesDataPermission.PermissionType.CUSTOMER, filterParam = SalesOutQueryPO.class)
    public IPage<SalesOut> queryPage(SalesOutQueryPO po) {
        // 设置默认排序参数
        if (StringUtils.isBlank(po.getOrderBy())) {
            po.setOrderBy("order_time");
        }
        if (StringUtils.isBlank(po.getOrderSort())) {
            po.setOrderSort("desc");
        }
        IPage<SalesOut> records = baseMapper.selectOrderListContainSalesMan(new Page(po.getPage(), po.getLimit()) ,po);
        List<SalesOut> salesOuts = records.getRecords();
        // 统计异常情况
        for(SalesOut so : salesOuts){
            BigDecimal money;
            List<SalesOutDetail> details = detailService.selectBySalesId(so.getId());
            List<OrderCost> costList = orderCostService.selectByMainId(so.getId());
            BigDecimal goodsMoney = details.stream().map(SalesOutDetail::getGoodsMoney).reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal packageMoney = details.stream().map(SalesOutDetail::getPackMoney).reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal costMoney = BigDecimal.ZERO;
            if(CollectionUtils.isNotEmpty(costList)){
                costMoney = costList.stream().map(OrderCost::getMoney).reduce(BigDecimal.ZERO, BigDecimal::add);
            }
            money = NumberUtil.add(goodsMoney, costMoney, packageMoney);
            so.setMoney(money);
            so.setCosts(costList);
            so.setTotalProfit(details.stream().map(SalesOutDetail::getTotalProfit).reduce(BigDecimal.ZERO, BigDecimal::add).setScale(2, RoundingMode.HALF_UP));
        }
        return records;
    }

    /**
     * 综合统计销售出库单信息（不分页）
     * 统计金额、数量、重量、利润以及按费用类型分组的其他费用
     *
     * @param po 查询参数
     * @return SalesOutPageVO 统计结果（仅包含统计字段，不含分页数据）
     */
    @Override
    @SalesDataPermission(type = SalesDataPermission.PermissionType.CUSTOMER, filterParam = SalesOutQueryPO.class)
    public SalesOutPageVO queryStatistics(SalesOutQueryPO po) {
        // 查询所有符合条件的销售出库单(不分页 - 使用很大的页面大小)
        IPage<SalesOut> page = baseMapper.selectOrderListContainSalesMan(
            new Page(1, Long.MAX_VALUE), po);
        List<SalesOut> salesOutList = page.getRecords();
        
        if (CollectionUtils.isEmpty(salesOutList)) {
            return new SalesOutPageVO();
        }
        
        // 收集所有订单ID
        List<Long> salesIds = salesOutList.stream()
            .map(SalesOut::getId)
            .collect(Collectors.toList());
        
        // 批量查询所有明细和费用 - 使用优化的查询方法（仅查询统计所需字段）
        List<SalesOutDetail> allDetails = detailService.selectBySalesIdsForStatistics(salesIds);
        List<OrderCost> allCosts = orderCostService.selectByMainIds(salesIds);
        
        // 按订单ID分组
        Map<Long, List<SalesOutDetail>> detailMap = allDetails.stream()
            .collect(Collectors.groupingBy(SalesOutDetail::getSalesId));
        Map<Long, List<OrderCost>> costMap = allCosts.stream()
            .collect(Collectors.groupingBy(OrderCost::getMainId));
        
        // 初始化统计数据
        Integer totalNumber = 0;
        BigDecimal totalWeight = BigDecimal.ZERO;
        BigDecimal totalMoney = BigDecimal.ZERO;
        BigDecimal totalProfit = BigDecimal.ZERO;
        BigDecimal totalOrderCost = BigDecimal.ZERO;
        Map<String, BigDecimal> costByType = new HashMap<>();
        Map<String, BigDecimal> totalMoneyByDate = new HashMap<>();
        
        // 遍历每个销售出库单进行统计
        for (SalesOut so : salesOutList) {
            // 从分组数据中获取，避免循环查询
            List<SalesOutDetail> details = detailMap.getOrDefault(so.getId(), new ArrayList<>());
            List<OrderCost> costList = costMap.getOrDefault(so.getId(), new ArrayList<>());
            
            // 1. 统计money - 金额
            BigDecimal goodsMoney = details.stream()
                .map(SalesOutDetail::getGoodsMoney)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal packageMoney = details.stream()
                .map(SalesOutDetail::getPackMoney)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
            
            BigDecimal costMoney = BigDecimal.ZERO;
            if (CollectionUtils.isNotEmpty(costList)) {
                costMoney = costList.stream()
                    .map(OrderCost::getMoney)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
                
                // 5. 统计其他费用,按费用类型分组
                for (OrderCost cost : costList) {
                    String costTypeName = cost.getCostClauseName();
                    BigDecimal costAmount = cost.getMoney();
                    if (costByType.containsKey(costTypeName)) {
                        costByType.put(costTypeName, costByType.get(costTypeName).add(costAmount));
                    } else {
                        costByType.put(costTypeName, costAmount);
                    }
                }
            }
            
            BigDecimal money = NumberUtil.add(goodsMoney, costMoney, packageMoney);
            totalMoney = totalMoney.add(money);
            String orderTime = DateUtil.formatDate(so.getOrderTime());;
            if (totalMoneyByDate.containsKey(orderTime)) {
                BigDecimal tmp = totalMoneyByDate.get(orderTime);
                totalMoneyByDate.put(orderTime, NumberUtil.add(money, tmp));
            } else {
                totalMoneyByDate.put(orderTime, money);
            }
            // 2. 统计number - 数量
            Integer orderNumber = details.stream()
                .map(SalesOutDetail::getNumber)
                .reduce(0, Integer::sum);
            totalNumber += orderNumber;
            
            // 3. 统计weight - 重量
            BigDecimal orderWeight = details.stream()
                .map(SalesOutDetail::getWeight)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
            totalWeight = totalWeight.add(orderWeight);
            
            // 4. 统计SalesOutDetail的总利润totalProfit
            BigDecimal orderProfit = details.stream()
                .map(SalesOutDetail::getTotalProfit)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
            totalProfit = totalProfit.add(orderProfit);
            
            // 累加其他费用总额
            totalOrderCost = totalOrderCost.add(costMoney);
        }
        
        // 对费用类型的金额保留两位小数
        for (Map.Entry<String, BigDecimal> entry : costByType.entrySet()) {
            costByType.put(entry.getKey(), entry.getValue().setScale(2, RoundingMode.HALF_UP));
        }
        
        // 封装统计结果（不包含分页数据）
        SalesOutPageVO statisticsVO = new SalesOutPageVO();
        statisticsVO.setTotalOrders(page.getTotal());
        statisticsVO.setTotalNumber(totalNumber);
        statisticsVO.setTotalWeight(totalWeight.setScale(2, RoundingMode.HALF_UP));
        statisticsVO.setTotalMoney(totalMoney.setScale(2, RoundingMode.HALF_UP));
        statisticsVO.setTotalProfit(totalProfit.setScale(2, RoundingMode.HALF_UP));
        statisticsVO.setTotalOrderCost(totalOrderCost.setScale(2, RoundingMode.HALF_UP));
        statisticsVO.setCostByType(costByType);
        statisticsVO.setTotalMoneyByDate(totalMoneyByDate);
        
        log.info("销售出库单综合统计完成，总订单数:{}, 总金额:{}, 总数量:{}, 总重量:{}, 总利润:{}, 其他费用总额:{}",
            page.getTotal(), statisticsVO.getTotalMoney(), statisticsVO.getTotalNumber(), 
            statisticsVO.getTotalWeight(), statisticsVO.getTotalProfit(),
            statisticsVO.getTotalOrderCost());
        
        return statisticsVO;
    }

    /**
     * 详情销售出库
     * @param id null
     * @return R
     * @author wrj
     * @since 2024/3/7 下午4:35
     */
    @Override
    public R detailSalesOut(Long id) {
        SalesOut salesOut = baseMapper.selectById(id);
        if (salesOut == null){
            return R.fail("订单不存在");
        }
        log.info("查询原订单的其他费用:{}", salesOut.getDocNo());
//        QueryWrapper<OrderCost> orderCostQueryWrapper = new QueryWrapper();
//        orderCostQueryWrapper.eq("main_id", salesOut.getId());
        List<OrderCost> orderCosts = orderCostService.selectByMainId(salesOut.getId());
        if (CollectionUtils.isNotEmpty(orderCosts)){
            salesOut.setCosts(orderCosts);
        }

        // 查询原订单的商品
        QueryWrapper<SalesOutDetail> detailQuery = new QueryWrapper();
        detailQuery.eq("sales_id", salesOut.getId());
        SalesOutStatisticsPO po = new SalesOutStatisticsPO();
        po.setSalesId(id);
        po.setPageSize(Long.MAX_VALUE);
        R r = salesOutStatisticsVO(po);
        IPage<SalesOutDetailVO> voiPage = (IPage<SalesOutDetailVO>) r.getData();
        List<SalesOutDetailVO> detailVOS = voiPage.getRecords();
        List<SalesOutDetail> details = new ArrayList<>();
        if(CollectionUtil.isNotEmpty(detailVOS)){
            for(SalesOutDetailVO vo : detailVOS){
                SalesOutDetail detail = new SalesOutDetail();
                BeanUtils.copyProperties(vo, detail);
                if(salesOut.getState() == 0 && NumberUtil.isLessOrEqual(detail.getPackUnitPrice(), BigDecimal.ZERO)){
                    BigDecimal unitPackagePrice = iUnitPackagePriceRecordService.queryUnitPackagePrice(detail.getCommodityId(), detail.getSpecsId(), salesOut.getCustomerId());
                    detail.setPackUnitPrice(unitPackagePrice);
                }
                if(ObjectUtil.isNull(vo.getWeight()) || NumberUtil.equals(vo.getWeight(), BigDecimal.ZERO)){
                    detail.setWeight(vo.getShares().stream().map(SalesOrderDetailStock::getWeight).reduce(BigDecimal.ZERO, BigDecimal::add));
                }
                details.add(detail);
            }
        }
            if(salesOut.getState() == 0){
                Integer lastNum = iTrainNumRecordService.queryTrainNum(salesOut.getCustomerId());
                salesOut.setTrainNum((lastNum + 1) + "");
            }
            salesOut.setCommodities(details);
        List<AllocationCollectionDetailWithSalesInfo> fcDetails = allocationCollectionService.findByOrderId(id);
        salesOut.setFcDetails(fcDetails);
        List<SalesOutCarryOn> carryOns = iSalesOutCarryOnService.selectBySalesOutId(salesOut.getId());
        salesOut.setCarryOnCommodities(carryOns);
        log.info("====>其他费用合集：{}", JSONArray.toJSONString(salesOut.getCosts()));
        return R.data(salesOut);
    }

    @Override
    public R detailsSalesOut(Long id) {
        List<SalesOutDetail> details = detailService.selectBySalesId(id);
        return R.data(details);
    }

    /**
     * 新增销售出库
     * @param salesOut null
     * @return R
     * @author wrj
     * @since 2024/3/7 下午4:35
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @SalesOutChangeTracking(value = "保存销售出库", type = SalesOutChangeTracking.OperationType.SAVE)
    public R saveSalesOut(SalesOut salesOut) throws Exception {
        // 新增入库采购收货主表
        if (CollectionUtils.isEmpty(salesOut.getCommodities())){
            throw new Exception("商品列表不能为空");
        }
        // 商品总数
        Integer number = 0;
        // 商品总重
        BigDecimal weight = BigDecimal.ZERO;
        BigDecimal money = BigDecimal.ZERO;
//        if (CollectionUtils.isNotEmpty(salesOut.getCosts())) {
//            for (OrderCost cost : salesOut.getCosts()) {
//                money = money.add(cost.getMoney());
//            }
//        }
        for (SalesOutDetail orderDetail: salesOut.getCommodities()) {
            number = number+orderDetail.getNumber();
            weight = weight.add(orderDetail.getWeight());
            money = money.add(orderDetail.getMoney()==null?BigDecimal.ZERO:orderDetail.getMoney());
        }
        salesOut.setNumber(number);
        salesOut.setWeight(weight);
        salesOut.setMoney(money);
        salesOut.setVerifyMonet(money);
        if (salesOut.getId() == null){
            salesOut.setDocNo(CommonConstant.XSCK+ baseMapper.getNextCode());
            save(salesOut);
        }else {
            // 修改主表数据
            updateById(salesOut);
            // 删除其他费用，商品信息，已经商品下的信息
            deleteMethod(salesOut);
        }
        // 入库采购收货主键
        Long mainId = salesOut.getId();
        // 保存订单下明细
        saveDetailInfo(mainId, salesOut);
        // 保存带货信息
        saveCarryOnCommodities(salesOut.getCarryOnCommodities(), mainId);
        return R.data(mainId);
    }

    private void saveCarryOnCommodities(List<SalesOutCarryOn> carryOnCommodities, Long mainId) {
        if(CollectionUtil.isNotEmpty(carryOnCommodities)){
            carryOnCommodities.forEach(el -> el.setSalesOutId(mainId));
            iSalesOutCarryOnService.saveBatch(carryOnCommodities);
        }
    }

    /**
     * 保存订单下明细
     * @param mainId
     * @param salesOut
     * @return
     */
    private R saveDetailInfo(Long mainId, SalesOut salesOut){
        // 保存入库代办交货的其他费用
        List<OrderCost> costs_all = new ArrayList<>();
        log.info("保存销售出库的其他费用:{}", JSON.toJSONString(salesOut.getDocNo()));
        if (CollectionUtils.isNotEmpty(salesOut.getCosts())){
            for (OrderCost cost :salesOut.getCosts()) {
                cost.setId(null);
                cost.setMainId(mainId);
                costs_all.add(cost);
            }
        }
        if (CollectionUtils.isNotEmpty(costs_all)){
            orderCostService.saveBatch(costs_all);
        }
        // 保存订单下明细
        log.info("保存订单下明细:{}", JSON.toJSONString(salesOut.getDocNo()));
        List<SalesOutDetail> commodities = salesOut.getCommodities();
        for (SalesOutDetail deliveryDetail: commodities) {
            deliveryDetail.setId(null);
            deliveryDetail.setSalesId(mainId);
            deliveryDetail.setTotalProfit(null);
            log.info("保存销售出库商品明细表");
            detailService.save(deliveryDetail);
            List<SalesOrderDetailStock> stocks = deliveryDetail.getStocks();
            if (CollectionUtils.isNotEmpty(stocks)){
                for (SalesOrderDetailStock detailStock:stocks) {
                    detailStock.setOrderId(mainId);
                    detailStock.setDetailId(deliveryDetail.getId());
                }
                log.info("保存销售出库商品对应库存明细表");
                detailStockService.saveBatch(stocks);
            }
        }
        return R.data(mainId);
    }

    /**
     * 提交销售出库
     * @param salesOut null
     * @return R
     * @author wrj
     * @since 2024/3/7 下午4:35
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @SalesOutChangeTracking(value = "提交销售出库", type = SalesOutChangeTracking.OperationType.SUBMIT)
    public R submitSalesOut(SalesOut salesOut) throws Exception {
        // 暂时没有部分库存
        salesOut.setState(1);
        R<SalesOut> result = saveSalesOut(salesOut);
        if (result.isSuccess()){
            // 判断来源，来源是订货则直接修改状态即可，因为走货阶段已经扣除的库存
//            if (salesOut.getOrderSource().intValue() == 1){
//                //  更新出库库存
//                log.info("销售出库提交，开始扣除库存：{}", JSON.toJSONString(salesOut));
//                saveOutStock(salesOut);
//            }
        }
        // 更新客商金额
        updateArrears(salesOut);
        // 更新客商往来明细
        saveAccountDetails(salesOut);
        // 保存商品的最近包装价格
        for (SalesOutDetail detail : salesOut.getCommodities()){
//            String key = detail.getCommodityId() + "-" + detail.getSalesId();
            BigDecimal packageUnit = detail.getPackUnitPrice();
//            unitPackageMap.put(key, packageUnit);
            iUnitPackagePriceRecordService.recordUnitPackagePrice(detail.getCommodityId(), detail.getSpecsId(), packageUnit, salesOut.getCustomerId());
        }
        // 保存出库车次
        try{
            String trainNumStr = salesOut.getTrainNum();
            Integer trainNum = 0;
            if(StringUtils.isNotBlank(trainNumStr)){
                if(trainNumStr.contains("-")){
                    trainNum = Integer.valueOf(trainNumStr.split("-")[0]);
                }else{
                    trainNum = Integer.valueOf(trainNumStr);
                }
            }
            iTrainNumRecordService.recordTrainNum(salesOut.getCustomerId(), trainNum);
        }catch (Exception e){
            e.printStackTrace();
        }
        effectSalesOrder(salesOut);
        return R.status(true);
    }

    /**
     * 影响销售订单
     * @param salesOut
     */
    private void effectSalesOrder(SalesOut salesOut) throws Exception {
        // 销售订单商品列表
        List<SalesOrderDetail> details = iSalesOrderDetailService.selectByOrderId(salesOut.getSalesOrderId());
        Map<String, SalesOrderDetail> detailMap = new HashMap<>();
        for(SalesOrderDetail detail : details){
            String key = detail.getCommodityId() + "-" + detail.getSpecsId();
            detailMap.put(key, detail);
        }
        // 关联销售订单变成完成状态
        salesOrderService.competeSalesOrder(salesOut.getSalesOrderId());
        for (SalesOutDetail detail : salesOut.getCommodities()){
            String key = detail.getCommodityId() + "-" + detail.getSpecsId();
            if(detailMap.containsKey(key)){
                SalesOrderDetail soDetail = detailMap.get(key);
                if(soDetail.getNumber() != detail.getNumber() || !NumberUtil.equals(soDetail.getWeight(), detail.getWeight())){
                    SalesOrderDetail salesOrderDetail = new SalesOrderDetail();
                    salesOrderDetail.setId(soDetail.getId());
                    salesOrderDetail.setNumber(detail.getNumber());
                    salesOrderDetail.setWeight(detail.getWeight());
                    salesOrderDetail.setStartNumber(detail.getNumber());
                    salesOrderDetail.setStartWeight(detail.getWeight());
                    iSalesOrderDetailService.updateById(salesOrderDetail);
                }
                detailMap.remove(key);
            }else{
                SalesOrderDetail salesOrderDetail = new SalesOrderDetail();
                BeanUtils.copyProperties(detail, salesOrderDetail);
                salesOrderDetail.setStartNumber(detail.getNumber());
                salesOrderDetail.setStartWeight(detail.getWeight());
                salesOrderDetail.setSalesId(salesOut.getSalesOrderId());
                iSalesOrderDetailService.save(salesOrderDetail);
            }
        }
        // 如果发现销售订单含有多余的商品，此时应该把商品的计划数量改成0。以显示异常状态
        if(CollectionUtil.isNotEmpty(detailMap)){
            Iterator <Map.Entry<String, SalesOrderDetail>> it = detailMap.entrySet().iterator();
            while (it.hasNext()) {
                SalesOrderDetail soDetail = it.next().getValue();
                SalesOrderDetail salesOrderDetail = new SalesOrderDetail();
                salesOrderDetail.setId(soDetail.getId());
                salesOrderDetail.setNumber(0);
                salesOrderDetail.setWeight(BigDecimal.ZERO);
                salesOrderDetail.setStartNumber(0);
                salesOrderDetail.setStartWeight(BigDecimal.ZERO);
                iSalesOrderDetailService.updateById(salesOrderDetail);
            }
        }
    }


    /**
     * 更新出库 库存
     * @param salesOut null
     * @return void
     * @author wrj
     * @since 2024/3/22 上午11:49
     */
//    @Deprecated
//    private void saveOutStock(SalesOut salesOut) throws Exception {
//        BigDecimal price_unit = salesOut.getMoney().divide(salesOut.getWeight(), 2, RoundingMode.HALF_UP);
//        // 更新出库库存
//        for (SalesOutDetail detail :salesOut.getCommodities()) {
//            if (CollectionUtils.isNotEmpty(detail.getStocks())){
//                for (SalesOrderDetailStock detailStock :detail.getStocks()) {
//                    stockService.saveOutStock(price_unit, detail.getOutNumber(), detail.getOutWeight(), detailStock.getStockId());
//                }
//            }
//        }
//    }

    /**
     * 更新待办,班组的应收金额
     *
     * @param salesOut null
     * @return void
     * @author wrj
     * @since 2024/3/22 上午11:47
     */
    private void updateArrears(SalesOut salesOut){
        // 更新待办的应收金额
        AddressBook addressBook = addressBookService.getById(salesOut.getCustomerId());
        BigDecimal arrears = addressBook.getArrears();
        // 加上该订单代办应收金额
        arrears = arrears.add(salesOut.getMoney());
        addressBook.setArrears(arrears);
        addressBookService.updateById(addressBook);
    }


    /**
     * 新增客商的往来明细
     * @param salesOut null
     * @return void
     * @author wrj
     * @since 2024/3/22 上午11:44
     */
    private void saveAccountDetails(SalesOut salesOut){
        // 生成代办的往来明细
        AccountDetails accountDetails = new AccountDetails();
        accountDetails.setStaffType(1); // 客商
        accountDetails.setPersonId(salesOut.getCustomerId());
        accountDetails.setDetailType(7); // 直销
        accountDetails.setOrderId(salesOut.getId());
        accountDetails.setOrderTime(salesOut.getOrderTime());
        accountDetails.setDocNo(salesOut.getDocNo());
        accountDetails.setMoney(salesOut.getMoney());
        log.info("销售出库保存客商的的往来明细：{}", JSON.toJSONString(accountDetails));
        QueryWrapper<AccountDetails> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(AccountDetails::getOrderId, salesOut.getId());
        wrapper.lambda().eq(AccountDetails::getStaffType, 1);
        wrapper.lambda().eq(AccountDetails::getDetailType, 7);
        accountDetailsService.saveOrUpdate(accountDetails, wrapper);
    }

    /**
     * 删除销售出库
     * @param id null
     * @return R
     * @author wrj
     * @since 2024/3/7 下午4:35
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R deleteSalesOut(Long id) throws Exception {
        SalesOut salesOut = baseMapper.selectById(id);
        if (salesOut == null){
            throw new Exception("订单不存在");
        }
        if (salesOut.getState().intValue() != 0){
            throw new Exception("订单不是未入库状态，不能删除");
        }
        baseMapper.deleteById(id);
        deleteMethod(salesOut);
        return R.status(true);
    }

    /**
     * 查询（未对账金额 > 0）可收款的销售订单
     * @param customerId null
     * @return R
     * @author wrj
     * @since 2024/4/15 上午9:31
     */
    @Override
    public List<SalesOut> canCollectionList(Long customerId) {
        // 查询原订单的商品
        QueryWrapper<SalesOut> detailQuery = new QueryWrapper();
        detailQuery.lambda().eq(SalesOut::getCustomerId, customerId);
        detailQuery.lambda().eq(SalesOut::getState, 1);
        detailQuery.lambda().gt(SalesOut::getVerifyMonet, 0);
        detailQuery.lambda().eq(SalesOut::getHedgingState, 0);
        List<SalesOut> details = baseMapper.selectList(detailQuery);
        return details;
    }

    /**
     * 删除主方法
     * @param salesOut
     * @return
     */
    private R deleteMethod(SalesOut salesOut) throws Exception {
        // 删除原订单的其他费用
        log.info("删除原订单的其他费用:{}", salesOut.getDocNo());
        Long salesOutId = salesOut.getId();
        orderCostService.deleteByMainId(salesOutId);
        // 删除出库单对应的带货信息
        iSalesOutCarryOnService.deleteBySalesOutId(salesOutId);
        QueryWrapper<SalesOutDetail> detailQueryWrapper = new QueryWrapper();
        detailQueryWrapper.eq("sales_id", salesOutId);
        List<SalesOutDetail> orderDetails = detailService.getBaseMapper().selectList(detailQueryWrapper);
        if (CollectionUtils.isNotEmpty(orderDetails)){
            List<Long> detailIds = orderDetails.stream().map(SalesOutDetail::getId).collect(Collectors.toList());

            QueryWrapper<SalesOutDetail> queryWrapper = new QueryWrapper();
            queryWrapper.eq("sales_id", salesOutId);
            log.info("删除销售订货商品明细");
            detailService.getBaseMapper().delete(queryWrapper);
            // todo 修改出库单为什么要删除销售订单的走货信息？
//            QueryWrapper<SalesOrderDetailStock> stockQueryWrapper = new QueryWrapper<>();
//            stockQueryWrapper.in("detail_id", detailIds);
//            log.info("删除销售订货商品下的库存关系表数据");
//            detailStockService.getBaseMapper().delete(stockQueryWrapper);

        }
        return R.status(true);
    }

    /**
     * 查询客商的可对账数据
     * @param po null
     * @return List<FinancialCollectionDetailVO>
     * @author wrj
     * @since 2024/4/28 下午2:03
     */
    @Override
    public List<FinancialCollectionDetailVO> reconciliationList(ReconciliationPO po) {
        List<FinancialCollectionDetailVO> vos = new ArrayList<>();
        
        // 用于去重的Set，存储已处理的销售出库单ID
        Set<Long> processedOrderIds = new HashSet<>();

        // 1. 首先查询该客商下所有已分配的明细数据（通过调用/allocation/collection/detail接口获取）
        // 获取该客户的所有分配回款记录ID
        QueryWrapper<AllocationCollection> collectionQueryWrapper = new QueryWrapper<>();
        collectionQueryWrapper.eq("customer_id", po.getCustomerId());
        collectionQueryWrapper.eq("id", po.getAllocationCollectionId());
        AllocationCollection collection = allocationCollectionService.getOne(collectionQueryWrapper);
        if(ObjectUtil.isNull(collection)){
            throw new RuntimeException("回款记录不存在");
        }
        // 获取分配回款详情
        R detailResult = allocationCollectionService.detailCollection(collection.getId());
        if (detailResult.isSuccess() && detailResult.getData() instanceof AllocationCollection) {
            AllocationCollection detailCollection = (AllocationCollection) detailResult.getData();
            if (CollectionUtils.isNotEmpty(detailCollection.getDetailList())) {
                for (AllocationCollectionDetail detail : detailCollection.getDetailList()) {
                    // 只处理AllocationCollectionDetailWithSalesInfo类型的明细
                    if (detail instanceof AllocationCollectionDetailWithSalesInfo) {
                        AllocationCollectionDetailWithSalesInfo detailWithSalesInfo = (AllocationCollectionDetailWithSalesInfo) detail;

                        // 检查是否已处理过该销售出库单ID
                        if (!processedOrderIds.contains(detailWithSalesInfo.getSalesOutId())) {
                            FinancialCollectionDetailVO vo = new FinancialCollectionDetailVO();
                            vo.setOrderId(detailWithSalesInfo.getSalesOutId());
                            vo.setDocNo(detailWithSalesInfo.getSalesOutDocNo());
                            vo.setLotNo(detailWithSalesInfo.getSalesOutDocNo());
                            vo.setOrderTime(detailWithSalesInfo.getOrderTime());
                            vo.setCustomerId(po.getCustomerId());
                            vo.setCustomerName(collection.getCustomerName());
                            // 已收金额
                            vo.setMoney(detailWithSalesInfo.getAllocatedAmount());
                            // 未对账金额为0，因为已分配的数据已经完成对账
                            vo.setVerifyMonet(BigDecimal.ZERO);
                            // 设置销售出库单金额
                            vo.setSalesOutAmount(detailWithSalesInfo.getSalesOrderAmount());
                            vo.setDeductionMoney(detailWithSalesInfo.getDeductionAmount());
                            vo.setDeductionReason(detailWithSalesInfo.getDeductionReason());
                            vo.setRemark(detailWithSalesInfo.getRemark());
                            // 从SalesOut对象获取已收金额
                            SalesOut salesOut = this.getById(detailWithSalesInfo.getSalesOutId());
                            if (salesOut != null) {
                                vo.setReceivedMoney(salesOut.getReceivedMoney());
                            }
                            vos.add(vo);

                            // 将销售出库单ID添加到已处理集合中
                            processedOrderIds.add(detailWithSalesInfo.getSalesOutId());
                        }
                    }
                }
            }
        }


        // 2. 然后查询该客商下所有符合对账条件的销售出库单数据
        List<SalesOut> list = this.list(
                new QueryWrapper<SalesOut>()
                        .in("state", 1)
                        .eq(ObjectUtil.isNotNull(po.getCustomerId()), "customer_id", po.getCustomerId())
                        .between("order_time", DateUtil.beginOfDay(po.getStartTime()), DateUtil.endOfDay(po.getEndTime()))
                        .eq("hedging_state", 0).orderByDesc("customer_name")
        );
        if (CollectionUtils.isNotEmpty(list)){
            for (SalesOut order:list) {
                // 检查是否已处理过该销售出库单ID（去重处理）
                if (!processedOrderIds.contains(order.getId())){
                    FinancialCollectionDetailVO vo = new FinancialCollectionDetailVO();
                    vo.setOrderId(order.getId());
                    vo.setDocNo(order.getDocNo());
                    vo.setLotNo(order.getDocNo());
                    vo.setOrderTime(order.getOrderTime());
                    vo.setTrainNum(order.getTrainNum());
                    vo.setLotNo(order.getLotNo());
                    vo.setCustomerId(order.getCustomerId());
                    vo.setCustomerName(order.getCustomerName());
                    //已收 = 应收 - 未收
                    vo.setReceivedMoney(order.getReceivedMoney());
                    vo.setVerifyMonet(order.getMoney());
                    // 设置销售出库单金额
                    vo.setSalesOutAmount(order.getMoney());
                    vos.add(vo);
                    
                    // 将销售出库单ID添加到已处理集合中
                    processedOrderIds.add(order.getId());
                }
            }
        }
        vos.sort(Comparator
                .comparing(FinancialCollectionDetailVO::getOrderTime,
                        Comparator.nullsLast(Comparator.naturalOrder())) // 处理 null 并自然排序
                .reversed());
        return vos;
    }

    /**
     * 查询未完成回款的销售出库单
     * @param customerId 客户ID
     * @return 未完成回款的销售出库单列表
     */
    public List<FinancialCollectionDetailVO> uncompletedCollectionList(Long customerId) {
        List<FinancialCollectionDetailVO> vos = new ArrayList<>();

        List<SalesOut> list = this.list(
                new QueryWrapper<SalesOut>()
                        .in("state", 1)
                        .eq(ObjectUtil.isNotNull(customerId), "customer_id", customerId)
                        .eq("hedging_state", 0) // 只查询未完成回款的出库单
                        .orderByDesc("customer_name")
        );
        if (CollectionUtils.isNotEmpty(list)){
            for (SalesOut order:list) {
                if (order.getVerifyMonet().compareTo(BigDecimal.ZERO) > 0){
                    FinancialCollectionDetailVO vo = new FinancialCollectionDetailVO();
                    vo.setOrderId(order.getId());
                    vo.setDocNo(order.getDocNo());
                    vo.setLotNo(order.getDocNo());
                    vo.setOrderTime(order.getOrderTime());
                    vo.setTrainNum(order.getTrainNum());
                    vo.setLotNo(order.getLotNo());
                    vo.setCustomerId(order.getCustomerId());
                    vo.setCustomerName(order.getCustomerName());
                    //已收 = 应收 - 未收
                    vo.setReceivedMoney(order.getMoney().subtract(order.getVerifyMonet()));
                    vo.setVerifyMonet(order.getMoney());
                    vos.add(vo);
                }
            }
        }

        return vos;
    }

    /**
     * 批量导入销售出库
     * @param orderList null
     * @return R
     * @author wrj
     * @since 2024/6/6 上午9:55
     */
    @Override
    public R uploadSalesOut(List<SalesOutImport> orderList) throws Exception  {
        List<String> failReasons = new ArrayList<>();
        String orderNo = orderList.get(0).getOrderNo();
        SalesOut salesOut = selectByOrderNo(orderNo);
        if(ObjectUtil.isNull(salesOut)){
            String failReason = "销售出库编码《" + orderNo + "》不存在";
            failReasons.add(failReason);
            return R.fail(failReasons);
        }
        // 客户只能是一个
//        Map<String, SalesOutImport> map = new HashMap<>();
        String key= null;
        // 验证商品是否存在
        List<SalesOutDetail> commodities = new ArrayList<>();
        for(SalesOutImport order : orderList){
            if(Objects.isNull(order.getNumber()) || order.getNumber() <= 0){
                String failReason = "商品《" + order.getCommodityName() + "》录入数量必须大于0";
                log.info(failReason);
                continue;
            }
            String commodityCode = order.getCommodityCode();
            if(StrUtil.isBlank(commodityCode)){
                continue;
            }
            Products product = productsService.queryByCommonCode(commodityCode);
            if(Objects.isNull(product)){
                String failReason = "商品编码《" + order.getCommodityCode() + "》不存在";
                failReasons.add(failReason);
            }else{
                SalesOutDetail detail = new SalesOutDetail();
//                    BigDecimal orderWeight = NumberUtil.mul(order.getStartNumber(), product.getStandardWeight());
                BeanUtils.copyProperties(order, detail);
                detail.setCommodityId(product.getId());
                detail.setCommodityName(product.getCommonName());
                detail.setCommodityCode(product.getCommonCode());
                detail.setStandard(product.getStandardWeight());
//                detail.setMode();
                commodities.add(detail);
            }
        }
        if(CollectionUtil.isNotEmpty(failReasons)){
            return R.data(failReasons);
        }else{
            // 导入订单数据
            SalesOut order = new SalesOut();
            BeanUtils.copyProperties(salesOut, order);
            order.setCommodities(commodities);
            saveSalesOut(order);
        }
        return R.status(true);
    }

    @Override
    @Transactional
    public R currentSalesOut(SalesOut salesOut) throws Exception {
        R<SalesOut> result = saveSalesOut(salesOut);
//        if (result.isSuccess()){
//            //  更新出库库存
//            log.info("销售出库提交，开始扣除库存：{}", JSON.toJSONString(salesOut));
////            saveOutStock(salesOut);
//        }
        // 更新客商金额
        updateArrears(salesOut);
        // 更新客商往来明细
        saveAccountDetails(salesOut);
        //
        effectSalesOrder(salesOut);
        return R.status(true);
    }

    @Override
    public SalesOut selectByOrderId(Long orderId) {
        QueryWrapper query = new QueryWrapper<SalesOut>();
        query.eq("sales_order_id" ,orderId);
        return this.baseMapper.selectOne(query);
    }
    @Override
    public void initUnitPackCost(){

    }

    @Override
    @SalesDataPermission(type = SalesDataPermission.PermissionType.CUSTOMER, filterParam = SalesOutStatisticsPO.class)
    public R sequenceToGeneral(SalesOutStatisticsPO po) {
        List<SalesOutGeneralVO> result = new ArrayList<>();
        po.setPageSize(Long.MAX_VALUE);
        List<SalesOutDetailVO> outList = baseMapper.selectDetailList(new Page(1, Long.MAX_VALUE), po);
        if(CollectionUtil.isNotEmpty(outList)){
            Map<String, List<SalesOutDetailVO>> listMap = outList.stream().collect(Collectors.groupingBy(SalesOutDetailVO ::getCustomerName));
            for(Map.Entry<String, List<SalesOutDetailVO>> en : listMap.entrySet()){
                SalesOutGeneralVO orderMap = new SalesOutGeneralVO();
                String customerName = en.getKey();
                List<SalesOutDetailVO> salesOuts = en.getValue();
                Set<String> lotNoSet = salesOuts.stream().map(el -> el.getLotNo()).collect(Collectors.toSet());
                orderMap.setCustomerName(customerName);
                orderMap.setCustomerId(salesOuts.get(0).getCustomerId());
                orderMap.setSalesmanName(salesOuts.get(0).getSalesmanName());
                orderMap.setSalesVolume(salesOuts.stream().map(SalesOutDetailVO::getMoney).reduce(BigDecimal.ZERO, BigDecimal::add));
                orderMap.setTotalWeight(salesOuts.stream().map(SalesOutDetailVO::getWeight).reduce(BigDecimal.ZERO, BigDecimal::add));
                orderMap.setTrainTimes(lotNoSet.size());
                orderMap.setTotalProfit(salesOuts.stream().map(SalesOutDetailVO::getTotalProfit).reduce(BigDecimal.ZERO, BigDecimal::add).setScale(2, RoundingMode.HALF_UP));
                Map<Long, BigDecimal> dMap = new HashMap<>();
                for(SalesOutDetailVO vo : salesOuts){
                    if(!dMap.containsKey(vo.getSalesId()) && NumberUtil.isGreater(vo.getDeductionMoney(), BigDecimal.ZERO)){
                        dMap.put(vo.getSalesId(), vo.getDeductionMoney());
                    }
                }
                Map<Long, BigDecimal> rMap = new HashMap<>();
                for(SalesOutDetailVO vo : salesOuts){
                    if(!rMap.containsKey(vo.getSalesId()) && NumberUtil.isGreater(vo.getReceivedMoney(), BigDecimal.ZERO)){
                        rMap.put(vo.getSalesId(), vo.getReceivedMoney());
                    }
                }
                orderMap.setDeductionMoney(dMap.values().stream().reduce(BigDecimal.ZERO, BigDecimal::add));
                orderMap.setReceivedMoney(rMap.values().stream().reduce(BigDecimal.ZERO, BigDecimal::add));
                orderMap.setNotReceivedMoney(NumberUtil.sub(orderMap.getSalesVolume(), orderMap.getReceivedMoney(), orderMap.getDeductionMoney()));
                result.add(orderMap);
            }

            Collections.sort(result, new Comparator<SalesOutGeneralVO>() {
                @Override
                public int compare(SalesOutGeneralVO o1, SalesOutGeneralVO o2) {
                    int re = NumberUtil.compare(o2.getSalesVolume().doubleValue(), o1.getSalesVolume().doubleValue());
                    return re;
                }
            });
        }
        return R.data(result);
    }

    @Override
    public R salesOutStatisticsVO(SalesOutStatisticsPO po) {
        IPage<SalesOutDetailVO> page = this.baseMapper.s1(new Page(po.getPageCurrent(), po.getPageSize()), po);
        List<SalesOutDetailVO> records = page.getRecords();
        List<Long> detailIds = records.stream().map(el -> el.getId()).collect(Collectors.toList());
        if(CollectionUtil.isEmpty(records)){
            return R.data(page);
        }
        List<SalesOutStatisticsVO> lm = this.baseMapper.s2(detailIds);
//        Map<Long, List<SalesOutStatisticsVO>> listMap = lm.stream().collect(Collectors.groupingBy(r -> r.getOutDetailId()));
        for(SalesOutDetailVO en : records){
            // 分离出 每件商品及对应的费用信息
            Long detailId = en.getId();
//            SalesOutDetailVO detailVO = records.stream().filter(el -> el.getId().equals(detailId)).findAny().get();
            List<SalesOutStatisticsVO> values = lm.stream().filter(el -> el.getOutDetailId().equals(detailId)).collect(Collectors.toList());
            BigDecimal costWeightForSalesTotal = BigDecimal.ZERO;
            Double packageCostForSales = 0d;
            // 总重量
            Double weightTotal = 0D;
            Integer numberTotal = 0;

            List<SalesOrderDetailStock> shares = new ArrayList<>();
//            Map<Long, List<SalesOutStatisticsVO>> stockBatchNoMap = values.stream().collect(Collectors.groupingBy(r -> r.getStockId()));
            for(SalesOutStatisticsVO batchMap : values){
//                List<SalesOutStatisticsVO> batchMapValue = batchMap.getValue();
                List<AgentDeliveryDetailPack> gMaterial = new ArrayList<>();
                // 归属包装成本的包装
                List<AgentDeliveryDetailPack> pMaterial = new ArrayList<>();
                String packageCostStr = batchMap.getPackageCost();
                List<AgentDeliveryDetailPack> materialList;
                if(batchMap.getStockBatchNo().startsWith(CommonConstant.JGDB)){
                    materialList = JSONArray.parseArray(packageCostStr, AgentDeliveryDetailPack.class);
                }else{
                    materialList = iAgentDeliveryDetailPackService.selectByDetailId(batchMap.getStockDetailId());
                }
                if(CollectionUtil.isNotEmpty(materialList)){
                    if ("小包装".equals(en.getCategory()) ) {
                        // 如果商品是小包装，所有包装归属于包装成本
                        pMaterial = materialList;
                    }else{
                        Map<Integer, List<AgentDeliveryDetailPack>> packMap = materialList.stream().filter(el -> el.getPackBelong() != null).collect(Collectors.groupingBy(el -> el.getPackBelong()));
                        gMaterial = CollectionUtil.isNotEmpty(packMap.get(0)) ? packMap.get(0) : gMaterial;
                        pMaterial = CollectionUtil.isNotEmpty(packMap.get(1)) ? packMap.get(1) : pMaterial;
                    }
                }
                // 如果包装材料不是空的，按照归属分类
//                Map<Integer, List<SalesOutStatisticsVO>> packMap = batchMapValue.stream().filter(el -> el.getPackBelong() != null).collect(Collectors.groupingBy(el -> el.getPackBelong()));
                // 归属蔬菜成本的包装
//                BigDecimal packageCostTotalBelong2Goods = gMaterial.stream().map(flow -> NumberUtil.mul(flow.getUnitPrice(), flow.getNumber())).reduce(BigDecimal.ZERO, BigDecimal::add);
//                BigDecimal packageCostTotal = pMaterial.stream().map(flow -> NumberUtil.mul(flow.getUnitPrice(), flow.getNumber())).reduce(BigDecimal.ZERO, BigDecimal::add);
                Double packageCostWeight = pMaterial.stream().mapToDouble(flow -> NumberUtil.mul(flow.getUnitPrice(), flow.getStandardNumber()).doubleValue()).sum();
                SalesOrderDetailStock detailStock = new SalesOrderDetailStock();
                detailStock.setStockBatchNo(batchMap.getStockBatchNo());
                detailStock.setNumber(batchMap.getShiftNumber());
                detailStock.setWeight(batchMap.getShiftWeight());
                detailStock.setCostWeight(batchMap.getCostWeight());
                detailStock.setAgentName(batchMap.getAgentName());
                detailStock.setOrderTime(batchMap.getOrderTime());
                detailStock.setStockOrderId(batchMap.getStockOrderId());
                detailStock.setPackageCostToShow(packageCostWeight);
                shares.add(detailStock);

//                for (SalesOutStatisticsVO stock : batchMapValue) {
//                    if (!NumberUtil.equals(BigDecimal.ZERO, detailVO.getStandard())) {
//                        // 如果是标重商品
//                        costWeightForSalesTotal = NumberUtil.add(costWeightForSalesTotal, NumberUtil.mul(stock.getCostWeight(), stock.getShiftNumber(), detailVO.getStandard()));
//                        packageCostForSales = packageCostForSales + packageCostWeight * stock.getShiftNumber();
//                        weightTotal = stock.getShiftNumber() * detailVO.getStandard().doubleValue() + weightTotal;
//                        numberTotal = numberTotal + stock.getShiftNumber();
//                    } else {
//                        // 如果是非标重的
//                        costWeightForSalesTotal = NumberUtil.add(costWeightForSalesTotal, NumberUtil.mul(stock.getCostWeight(), detailVO.getWeight()));
//                        packageCostForSales = packageCostForSales + packageCostWeight * stock.getShiftNumber();
//                        weightTotal = detailVO.getWeight().doubleValue() + weightTotal;
//                        numberTotal = numberTotal + stock.getShiftNumber();
//                    }
//                    weightTotal = weightTotal == 0D ? 1D : weightTotal;
//                    numberTotal = numberTotal == 0 ? 1 : numberTotal;
//                    // 蔬菜成本
//                    BigDecimal costForSales = NumberUtil.div(costWeightForSalesTotal, weightTotal);
//                    // 包装成本
//                    BigDecimal packageForSales = NumberUtil.div(packageCostForSales, new BigDecimal(numberTotal));
//                    if ("小包装".equals(detailVO.getCategory()) ) {
//                        // 小包装不再单独计算包装成本，而是把包装成本合计到蔬菜成本里
//                        BigDecimal acPackageForSales = NumberUtil.div(packageCostTotal, batchMapValue.get(0).getInitWeight());
//                        costForSales = NumberUtil.add(costForSales, acPackageForSales);
//                        packageForSales = BigDecimal.ZERO;
//                    } else {
//                        // 如果不是小包装
////                        BigDecimal acPackageForSales = NumberUtil.div(packageCostTotalBelong2Goods, batchMapValue.get(0).getInitWeight());
////                        costForSales = NumberUtil.add(costForSales, acPackageForSales);
//                    }
//                    detailVO.setPackageCostForSales(packageForSales);
//                    detailVO.setCostWeightForSales(costForSales);
//                }
            }
            en.setShares(shares);
        }
//        for(SalesOutDetailVO vo : page.getRecords()){
//            vo.calculateProfit();
//        }
        return R.data(page);
    }

    public List<SalesOutDetailVO> calculateCostWeight(Long outOrderId) {
        SalesOutStatisticsPO po = new SalesOutStatisticsPO();
        po.setSalesId(outOrderId);
        po.setPageSize(Long.MAX_VALUE);
        IPage<SalesOutDetailVO> page = this.baseMapper.s1(new Page(po.getPageCurrent(), po.getPageSize()), po);
        List<SalesOutDetailVO> records = page.getRecords();
        List<Long> detailIds = records.stream().map(el -> el.getId()).collect(Collectors.toList());
        if(CollectionUtil.isEmpty(records)){
            return records;
        }
        List<SalesOutStatisticsVO> lm = this.baseMapper.s2(detailIds);
//        Map<Long, List<SalesOutStatisticsVO>> listMap = lm.stream().collect(Collectors.groupingBy(r -> r.getOutDetailId()));
        for(SalesOutDetailVO en : records){
            // 分离出 每件商品及对应的费用信息
            Long detailId = en.getId();
            List<SalesOutStatisticsVO> values = lm.stream().filter(el -> el.getOutDetailId().equals(detailId)).collect(Collectors.toList());
//            List<SalesOutStatisticsVO> values = en.getValue();
            BigDecimal costWeightForSalesTotal = BigDecimal.ZERO;
            Double packageCostForSales = 0d;
            // 总重量
            Double weightTotal = 0D;
            Integer numberTotal = 0;

            List<SalesOrderDetailStock> shares = new ArrayList<>();
//            Map<Long, List<SalesOutStatisticsVO>> stockBatchNoMap = values.stream().collect(Collectors.groupingBy(r -> r.getStockId()));
            for(SalesOutStatisticsVO batchMap : values){
//                List<SalesOutStatisticsVO> batchMapValue = batchMap.getValue();
                // 如果包装材料不是空的，按照归属分类
                List<AgentDeliveryDetailPack> gMaterial = new ArrayList<>();
                List<AgentDeliveryDetailPack> pMaterial = new ArrayList<>();
                String packageCostStr = batchMap.getPackageCost();
                List<AgentDeliveryDetailPack> materialList;
                if(batchMap.getStockBatchNo().startsWith(CommonConstant.JGDB)){
                    materialList = JSONArray.parseArray(packageCostStr, AgentDeliveryDetailPack.class);
                }else{
                    materialList = iAgentDeliveryDetailPackService.selectByDetailId(batchMap.getStockDetailId());
                }
                if(CollectionUtil.isNotEmpty(materialList)){
                    if ("小包装".equals(en.getCategory()) ) {
                        // 如果商品是小包装，所有包装归属于包装成本
                        pMaterial = materialList;
                    }else{
                        Map<Integer, List<AgentDeliveryDetailPack>> packMap = materialList.stream().filter(el -> el.getPackBelong() != null).collect(Collectors.groupingBy(el -> el.getPackBelong()));
                        gMaterial = CollectionUtil.isNotEmpty(packMap.get(0)) ? packMap.get(0) : gMaterial;
                        pMaterial = CollectionUtil.isNotEmpty(packMap.get(1)) ? packMap.get(1) : pMaterial;
                    }
                }
                BigDecimal packageCostTotalBelong2Goods = gMaterial.stream().map(flow -> NumberUtil.mul(flow.getUnitPrice(), flow.getNumber())).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal packageCostTotal = pMaterial.stream().map(flow -> NumberUtil.mul(flow.getUnitPrice(), flow.getNumber())).reduce(BigDecimal.ZERO, BigDecimal::add);
                Double packageCostWeight = pMaterial.stream().mapToDouble(flow -> NumberUtil.mul(flow.getUnitPrice(), flow.getStandardNumber()).doubleValue()).sum();
                SalesOrderDetailStock detailStock = new SalesOrderDetailStock();
                detailStock.setStockBatchNo(batchMap.getStockBatchNo());
                detailStock.setNumber(batchMap.getShiftNumber());
                detailStock.setWeight(batchMap.getShiftWeight());
                detailStock.setCostWeight(batchMap.getCostWeight());
                detailStock.setAgentName(batchMap.getAgentName());
                detailStock.setOrderTime(batchMap.getOrderTime());
                detailStock.setStockOrderId(batchMap.getStockOrderId());
                detailStock.setPackageCostToShow(packageCostWeight);
                shares.add(detailStock);

//                for (SalesOutStatisticsVO stock : batchMapValue) {
                SalesOutStatisticsVO stock = batchMap;
                if (!NumberUtil.equals(BigDecimal.ZERO, en.getStandard())) {
                        // 如果是标重商品
                        costWeightForSalesTotal = NumberUtil.add(costWeightForSalesTotal, NumberUtil.mul(stock.getCostWeight(), stock.getShiftNumber(), en.getStandard()));
                        packageCostForSales = packageCostForSales + packageCostWeight * stock.getShiftNumber();
                        weightTotal = stock.getShiftNumber() * en.getStandard().doubleValue() + weightTotal;
                        numberTotal = numberTotal + stock.getShiftNumber();
                    } else {
                        // 如果是非标重的
                        costWeightForSalesTotal = NumberUtil.add(costWeightForSalesTotal, NumberUtil.mul(stock.getCostWeight(), stock.getShiftWeight()));
                        packageCostForSales = packageCostForSales + packageCostWeight * stock.getShiftNumber();
                        weightTotal = stock.getShiftWeight().doubleValue() + weightTotal;
                        numberTotal = numberTotal + stock.getShiftNumber();
                    }
                    weightTotal = weightTotal == 0D ? 1D : weightTotal;
                    numberTotal = numberTotal == 0 ? 1 : numberTotal;
                    // 蔬菜成本
                    BigDecimal costForSales = NumberUtil.div(costWeightForSalesTotal, weightTotal);
                    // 包装成本
                    BigDecimal packageForSales = NumberUtil.div(packageCostForSales, new BigDecimal(numberTotal));
                    if ("小包装".equals(en.getCategory()) ) {
                        // 小包装不再单独计算包装成本，而是把包装成本合计到蔬菜成本里
                        BigDecimal acPackageForSales = NumberUtil.div(packageCostTotal, stock.getInitWeight());
                        costForSales = NumberUtil.add(costForSales, acPackageForSales);
                        packageForSales = BigDecimal.ZERO;
                    } else {
                        // 如果不是小包装
//                        BigDecimal acPackageForSales = NumberUtil.div(packageCostTotalBelong2Goods, batchMapValue.get(0).getInitWeight());
//                        costForSales = NumberUtil.add(costForSales, acPackageForSales);
                    }
                    en.setPackageCostForSales(packageForSales);
                    en.setCostWeightForSales(costForSales);
//                }
            }
            en.setShares(shares);
        }
//        for(SalesOutDetailVO vo : page.getRecords()){
//            vo.calculateProfit();
//        }
        return records;
    }




    @Override
    public SalesOut selectByOrderNo(String orderNo) {
        QueryWrapper query = new QueryWrapper<SalesOut>();
        query.eq("doc_no" ,orderNo);
        return this.baseMapper.selectOne(query);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public R oneClickOut(Long salesOrderId) throws Exception {
        R<SalesOut> salesR = detailSalesOut(salesOrderId);
        SalesOut salesOut = salesR.getData();
        List<SalesOutDetail> commodities = salesOut.getCommodities();
        for(SalesOutDetail detail : commodities){
            if(ObjectUtil.isNotNull(detail.getCostWeightToShow())){
                detail.setUnitPrice(detail.getCostWeightToShow());
                detail.setGoodsMoney(NumberUtil.mul(detail.getWeight(), detail.getUnitPrice()));
            }
            if(ObjectUtil.isNotNull(detail.getPackageCostToShow())){
                detail.setPackUnitPrice(detail.getPackageCostToShow());
            }
            log.info("{}的商品价格-{}, 包装价格{}", detail.getId(), detail.getCostWeightToShow(), detail.getPackageCostToShow());
        }
        submitSalesOut(salesOut);
        return R.success("一键出库成功");
    }

    @Override
    public void checkOrderCostByOrderId(Long orderId) {
        log.info("开始处理销售订单 {} 的成本检查", orderId);
        
        // 根据销售订单找出销售出库单
        SalesOut out = selectByOrderId(orderId);
        if(ObjectUtil.isNull(out)){
            log.info("根据订单ID {} 没有找到销售出库单", orderId);
            return;
        }
        log.info("找到销售出库单，ID: {}, 单据编号: {}", out.getId(), out.getDocNo());
        List<SalesOutDetailVO>  commodities = calculateCostWeight(out.getId());
        if(CollectionUtil.isEmpty(commodities)) {
            log.info("销售出库单 {} 没有需要处理的明细", out.getId());
            return;
        }
        log.info("计算得到 {} 条明细的成本信息，开始更新", commodities.size());
        
//        List<SalesOutDetailVO> commodities = salesR.getData().getCommodities();
        int successCount = 0;
        int failCount = 0;
        
        for(SalesOutDetailVO detail : commodities){
            Long detailId = detail.getId();
            try {
                BigDecimal costWeightForSales = detail.getCostWeightForSales().setScale(6, RoundingMode.HALF_UP);
                BigDecimal packageCostForSales = detail.getPackageCostForSales().setScale(6, RoundingMode.HALF_UP);

                SalesOutDetail outDetail = detailService.getById(detailId);
                if(Objects.isNull(outDetail)){
                    log.info("根据{}没有找到销售出库单详情", detailId);
                    continue;
                }
                if(NumberUtil.equals(outDetail.getCostWeightToShow(), costWeightForSales) && NumberUtil.equals(outDetail.getPackageCostToShow(), packageCostForSales) ){
                    log.info("销售出库单详情id {} 的成本信息没有发生变化，蔬菜成本：{}，包装成本{}", detailId, costWeightForSales, packageCostForSales);
                }else{
                    SalesOutDetail updater = new SalesOutDetail();
                    updater.setId(detailId);
                    updater.setCostWeightToShow(costWeightForSales);
                    updater.setPackageCostToShow(packageCostForSales);
                    log.info("注意了！！准备更新明细 {} 的成本信息，蔬菜成本：{}，包装成本{}", detailId, costWeightForSales, packageCostForSales);
                    boolean updateResult = detailService.updateById(updater);

                    if(updateResult) {
                        log.debug("明细 {} 成本信息更新成功", detailId);
                        successCount++;
                    } else {
                        log.warn("明细 {} 成本信息更新失败", detailId);
                        failCount++;
                    }
                }
            } catch (Exception e) {
                log.error("处理明细 {} 时发生异常", detailId, e);
                failCount++;
            }
        }
        
        log.info("销售订单 {} 成本检查处理完成，成功更新 {} 条明细，失败 {} 条明细", orderId, successCount, failCount);
    }

    @Override
    public void checkOrderCostByStockId(Long stockId) {
        List<SalesOrderDetailStock> stockList = detailStockService.selectByStockId(stockId);
        if(CollectionUtil.isNotEmpty(stockList)){
            for(SalesOrderDetailStock stock : stockList){
                checkOrderCostByOrderId(stock.getOrderId());
            }
        }
    }

    @Override
    public List<SalesOutDetailExport> exportStatisticsSalesOut(SalesOutStatisticsPO po) {
        if(Objects.isNull(po.getStartTime()) || Objects.isNull(po.getEndTime())){
            throw new RuntimeException("开始日期或者结束日期不能为空");
        }
        if(DateUtil.between(po.getEndTime(), po.getStartTime(), DateUnit.DAY) > 31){
            throw new RuntimeException("查询日期不能超过31天");
        }
        IPage<SalesOutDetailVO> page = this.baseMapper.s1(new Page(1, Long.MAX_VALUE), po);
        List<SalesOutDetailExport> records = new ArrayList<>();
        if(CollectionUtil.isNotEmpty(page.getRecords())){
            for(SalesOutDetailVO vo : page.getRecords()){
                SalesOutDetailExport detailExport = new SalesOutDetailExport();
                BeanUtils.copyProperties(vo, detailExport);
                detailExport.setOrderTime(DateUtil.formatDate(vo.getOrderTime()));
                records.add(detailExport);
            }
        }
        return records;
    }

    @Override
    @SalesDataPermission(type = SalesDataPermission.PermissionType.CUSTOMER, filterParam = SalesOutStatisticsPO.class)
    public R sequenceToDetail(SalesOutStatisticsPO po) {
        List<SalesOutGeneralVO> result = new ArrayList<>();
        po.setPageSize(Long.MAX_VALUE);
        List<SalesOutDetailVO> outList = baseMapper.selectDetailList(new Page(1, Long.MAX_VALUE), po);
        if(CollectionUtil.isNotEmpty(outList)){
            Map<String, List<SalesOutDetailVO>> listMap = outList.stream().collect(Collectors.groupingBy(SalesOutDetailVO ::getDocNo));
            for(Map.Entry<String, List<SalesOutDetailVO>> en : listMap.entrySet()){
                SalesOutGeneralVO orderMap = new SalesOutGeneralVO();
                String docNo = en.getKey();
                List<SalesOutDetailVO> salesOuts = en.getValue();
                Set<String> lotNoSet = salesOuts.stream().map(el -> el.getLotNo()).collect(Collectors.toSet());
                orderMap.setCustomerId(salesOuts.get(0).getCustomerId());
                orderMap.setDocNo(docNo);
                orderMap.setOrderTime(salesOuts.get(0).getOrderTime());
                orderMap.setSalesId(salesOuts.get(0).getSalesId());
                orderMap.setSalesmanName(salesOuts.get(0).getSalesmanName());
                orderMap.setSalesVolume(salesOuts.stream().map(SalesOutDetailVO::getMoney).reduce(BigDecimal.ZERO, BigDecimal::add));
                orderMap.setTotalWeight(salesOuts.stream().map(SalesOutDetailVO::getWeight).reduce(BigDecimal.ZERO, BigDecimal::add));
                orderMap.setTrainTimes(lotNoSet.size());
                orderMap.setTotalProfit(salesOuts.stream().map(SalesOutDetailVO::getTotalProfit).reduce(BigDecimal.ZERO, BigDecimal::add).setScale(2, RoundingMode.HALF_UP));
                Map<Long, BigDecimal> dMap = new HashMap<>();
                for(SalesOutDetailVO vo : salesOuts){
                    if(!dMap.containsKey(vo.getSalesId()) && NumberUtil.isGreater(vo.getDeductionMoney(), BigDecimal.ZERO)){
                        dMap.put(vo.getSalesId(), vo.getDeductionMoney());
                    }
                }
                Map<Long, BigDecimal> rMap = new HashMap<>();
                for(SalesOutDetailVO vo : salesOuts){
                    if(!rMap.containsKey(vo.getSalesId()) && NumberUtil.isGreater(vo.getReceivedMoney(), BigDecimal.ZERO)){
                        rMap.put(vo.getSalesId(), vo.getReceivedMoney());
                    }
                }
                orderMap.setDeductionMoney(dMap.values().stream().reduce(BigDecimal.ZERO, BigDecimal::add));
                orderMap.setReceivedMoney(rMap.values().stream().reduce(BigDecimal.ZERO, BigDecimal::add));
                orderMap.setNotReceivedMoney(NumberUtil.sub(orderMap.getSalesVolume(), orderMap.getReceivedMoney(), orderMap.getDeductionMoney()));
                result.add(orderMap);
            }

            Collections.sort(result, new Comparator<SalesOutGeneralVO>() {
                @Override
                public int compare(SalesOutGeneralVO o1, SalesOutGeneralVO o2) {
                    return DateUtil.compare(o2.getOrderTime(), o1.getOrderTime());
                }
            });
        }
        return R.data(result);
    }

    private BigDecimal getActWeight(SalesOutDetail detail) {
        BigDecimal actWeight;
        if(BigDecimal.ZERO.equals(detail.getStandard())){
            actWeight = detail.getWeight();
        }else{
            actWeight = NumberUtil.mul(detail.getStandard(), detail.getNumber());
        }
        return actWeight;
    }

    /**
     * 更新销售出库单对冲状态
     * @param po 更新对冲状态参数对象
     * @return R
     * @author Qoder
     * @since 2025/10/21
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R updateHedgingState(UpdateHedgingStatePO po) {
        log.info("更新销售出库单对冲状态，po:{}", JSON.toJSONString(po));
        
        // 参数校验
        if (po.getId() == null) {
            return R.fail("销售出库单ID不能为空");
        }
        if (po.getHedgingState() == null) {
            return R.fail("对冲状态不能为空");
        }
        if (po.getHedgingState() != 0 && po.getHedgingState() != 1) {
            return R.fail("对冲状态只能为0（未对冲）或1（已对冲）");
        }
        
        // 查询销售出库单
        SalesOut salesOut = baseMapper.selectById(po.getId());
        if (salesOut == null) {
            return R.fail("销售出库单不存在");
        }
        
        // 更新对冲状态和备注
        salesOut.setHedgingState(po.getHedgingState());
        if (StrUtil.isNotBlank(po.getRemark())) {
            salesOut.setRemark(po.getRemark());
        }
        
        // 保存更新
        boolean success = updateById(salesOut);
        if (!success) {
            return R.fail("更新销售出库单对冲状态失败");
        }
        
        log.info("销售出库单对冲状态更新成功，单据编号:{}, 新状态:{}", salesOut.getDocNo(), po.getHedgingState());
        return R.success("更新对冲状态成功");
    }

    /**
     * 更新销售出库单和销售订单的订单时间
     * @param po 更新订单时间参数对象
     * @return R
     * @author Qoder
     * @since 2025/10/31
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R updateOrderTime(UpdateOrderTimePO po) {
        log.info("更新销售出库单订单时间，po:{}", JSON.toJSONString(po));
        
        // 参数校验
        if (po.getId() == null) {
            return R.fail("销售出库单ID不能为空");
        }
        if (po.getOrderTime() == null) {
            return R.fail("订单时间不能为空");
        }

        // 查询销售出库单
        SalesOut salesOut = baseMapper.selectById(po.getId());
        if (salesOut == null) {
            return R.fail("销售出库单不存在");
        }

        // 更新销售出库单的订单时间
        salesOut.setOrderTime(po.getOrderTime());
        boolean success = updateById(salesOut);
        if (!success) {
            return R.fail("更新销售出库单订单时间失败");
        }

        // 如果存在关联的销售订单，则同步更新销售订单的订单时间
        if (salesOut.getSalesOrderId() != null) {
            SalesOrder salesOrder = salesOrderService.getById(salesOut.getSalesOrderId());
            if (salesOrder != null) {
                salesOrder.setOrderTime(po.getOrderTime());
                boolean orderSuccess = salesOrderService.updateById(salesOrder);
                if (!orderSuccess) {
                    log.warn("更新销售订单[id={}]订单时间失败", salesOut.getSalesOrderId());
                } else {
                    log.info("销售订单[id={}]订单时间更新成功", salesOut.getSalesOrderId());
                }
            }
        }

        log.info("销售出库单订单时间更新成功，单据编号:{}", salesOut.getDocNo());
        return R.success("订单时间更新成功");
    }
}
